﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SudokuHelper.Model.Values;

namespace SudokuHelper.Model {
    public class VarDesk : SudokuDesk, IValueVisitor {
        #region Constructors

        public VarDesk() {
            for (int i = 0; i < 9; i++){
                for (int j = 0; j < 9; j++){
                    Cells[i, j] = new ValueRange();
                }
            }
        }

        #endregion

        #region Overrides of SudokuDesk

        public override IValue this[int row, int col] {
            set {
                base[row, col] = value;
                if(value is UserValue) {
                    Recalculate(row, col, (value as UserValue).Value);
                }
            }
        }

        #endregion

        #region Recalculation

        private void Recalculate(int row, int col, byte? value) {
            //Recalculate available values to cell area
            this.ForeachInArea(row, col,
                               (r, c) => base[r, c].Accept(this, new CellData {Row = r, Col = c, Value = value}));

            TestAvailableValues();
        }

        /// <summary>
        /// Test available values to detect possible alone value
        /// </summary>
        private void TestAvailableValues() {
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    if (!(base[i, j] is ValueRange))
                        continue;

                    var avails = (base[i, j] as ValueRange).AvailableValues;

                    foreach (var avail in avails){
                        bool isAlone = true;

                        var detector = new ValueDetector(avail);
                        this.ForeachInArea(i, j,
                            delegate(int r, int c) {
                                if ((bool) base[r, c].Accept(detector, null))
                                    isAlone = false;
                            });

                        if (isAlone)
                            this[i, j] = new CalculatedValue(avail);
                    }
                }
            }
        }

        #endregion

        #region Implementation of IValueVisitor

        public object Visit(UserValue value, object arg) {
            return null;
        }

        public object Visit(CalculatedValue value, object arg) {
            return null;
        }

        public object Visit(ValueRange valueRange, object arg) {
            CellData data = (CellData) arg;

            valueRange.AvailableValues.Remove(data.Value.Value);
            RaiseCellValueChanged(data.Row, data.Col);

            return true;
        }

        #endregion
    }

    internal struct CellData {
        public byte? Value;
        public int Row, Col;
    }
}